package com.zzy.broker.handler;

import com.zzy.broker.server.message.MqttRawMessage;
import com.zzy.broker.session.ClientSession;
import com.zzy.broker.session.SessionManager;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.nio.charset.StandardCharsets;

public class ConnectHandler extends SimpleChannelInboundHandler<MqttRawMessage> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MqttRawMessage msg) throws Exception {
        // 只处理 CONNECT 消息 (固定报头 = 0x10)
        byte messageType = (byte) ((msg.header >> 4) & 0x0F);
        
        if (messageType != 1) {  // 1 = CONNECT, 2 = CONNACK, 3 = PUBLISH, 8 = SUBSCRIBE, etc.
            // 不是 CONNECT 消息，传递给下一个 Handler
            System.out.println("Received non-CONNECT message, type: " + messageType + ", passing to next handler");
            ctx.fireChannelRead(msg);
            return;
        }
        
        System.out.println("=== MQTT CONNECT Message Debug ===");
        System.out.println("Fixed Header: 0x" + String.format("%02X", msg.header));
        System.out.println("Payload Length: " + msg.payload.length);
        System.out.println("Payload (hex): " + bytesToHex(msg.payload));
        
        // 解析 CONNECT 消息
        ByteBuf payload = Unpooled.wrappedBuffer(msg.payload);
        
        try {
            // 1. 读取协议名称（UTF-8 字符串，前2字节为长度）
            if (payload.readableBytes() < 2) {
                throw new IllegalArgumentException("Insufficient data for protocol name length");
            }
            int protocolNameLen = payload.readUnsignedShort();
            System.out.println("Protocol Name Length: " + protocolNameLen + ", Readable: " + payload.readableBytes());
            
            if (payload.readableBytes() < protocolNameLen) {
                throw new IllegalArgumentException("Insufficient data for protocol name");
            }
            byte[] protocolNameBytes = new byte[protocolNameLen];
            payload.readBytes(protocolNameBytes);
            String protocolName = new String(protocolNameBytes, StandardCharsets.UTF_8);
            System.out.println("Protocol Name: " + protocolName);
            
            // 2. 读取协议级别
            if (payload.readableBytes() < 1) {
                throw new IllegalArgumentException("Insufficient data for protocol level");
            }
            int protocolLevel = payload.readUnsignedByte();
            System.out.println("Protocol Level: " + protocolLevel);
            
            // 3. 读取连接标志
            if (payload.readableBytes() < 1) {
                throw new IllegalArgumentException("Insufficient data for connect flags");
            }
            int connectFlags = payload.readUnsignedByte();
            System.out.println("Connect Flags: 0x" + String.format("%02X", connectFlags));
            
            boolean cleanSession = (connectFlags & 0x02) != 0;
            boolean willFlag = (connectFlags & 0x04) != 0;
            int willQoS = (connectFlags & 0x18) >> 3;
            boolean willRetain = (connectFlags & 0x20) != 0;
            boolean passwordFlag = (connectFlags & 0x40) != 0;
            boolean usernameFlag = (connectFlags & 0x80) != 0;
            
            System.out.println("Clean Session: " + cleanSession);
            System.out.println("Will Flag: " + willFlag + ", Username Flag: " + usernameFlag + ", Password Flag: " + passwordFlag);
            
            // 4. 读取保活时间（秒）
            if (payload.readableBytes() < 2) {
                throw new IllegalArgumentException("Insufficient data for keep alive");
            }
            int keepAlive = payload.readUnsignedShort();
            System.out.println("Keep Alive: " + keepAlive);
            
            // MQTT 5.0: 读取 Properties（可变报头的属性）
            if (protocolLevel == 5) {
                System.out.println("--- MQTT 5.0 detected, reading Properties ---");
                int propertiesLength = decodeVariableByteInteger(payload);
                System.out.println("Properties Length: " + propertiesLength);
                
                if (payload.readableBytes() < propertiesLength) {
                    throw new IllegalArgumentException("Insufficient data for properties");
                }
                // 跳过 Properties（暂不解析具体属性）
                payload.skipBytes(propertiesLength);
            }
            
            System.out.println("--- Before reading Client ID, readable bytes: " + payload.readableBytes() + " ---");
            
            // 5. 读取客户端ID
            if (payload.readableBytes() < 2) {
                throw new IllegalArgumentException("Insufficient data for client ID length");
            }
            int clientIdLen = payload.readUnsignedShort();
            System.out.println("Client ID Length: " + clientIdLen + ", Readable: " + payload.readableBytes());
            
            if (payload.readableBytes() < clientIdLen) {
                throw new IllegalArgumentException("Insufficient data for client ID, expected: " + clientIdLen + ", available: " + payload.readableBytes());
            }
            byte[] clientIdBytes = new byte[clientIdLen];
            payload.readBytes(clientIdBytes);
            String clientId = new String(clientIdBytes, StandardCharsets.UTF_8);
            System.out.println("Client ID: " + clientId);
            
            // 6. 读取 Will Topic 和 Will Message（如果有）
            String willTopic = null;
            String willMessage = null;
            if (willFlag) {
                // MQTT 5.0: Will Properties
                if (protocolLevel == 5) {
                    int willPropertiesLength = decodeVariableByteInteger(payload);
                    System.out.println("Will Properties Length: " + willPropertiesLength);
                    if (payload.readableBytes() < willPropertiesLength) {
                        throw new IllegalArgumentException("Insufficient data for will properties");
                    }
                    payload.skipBytes(willPropertiesLength);
                }
                
                if (payload.readableBytes() < 2) {
                    throw new IllegalArgumentException("Insufficient data for will topic length");
                }
                int willTopicLen = payload.readUnsignedShort();
                if (payload.readableBytes() < willTopicLen) {
                    throw new IllegalArgumentException("Insufficient data for will topic");
                }
                byte[] willTopicBytes = new byte[willTopicLen];
                payload.readBytes(willTopicBytes);
                willTopic = new String(willTopicBytes, StandardCharsets.UTF_8);
                
                if (payload.readableBytes() < 2) {
                    throw new IllegalArgumentException("Insufficient data for will message length");
                }
                int willMessageLen = payload.readUnsignedShort();
                if (payload.readableBytes() < willMessageLen) {
                    throw new IllegalArgumentException("Insufficient data for will message");
                }
                byte[] willMessageBytes = new byte[willMessageLen];
                payload.readBytes(willMessageBytes);
                willMessage = new String(willMessageBytes, StandardCharsets.UTF_8);
                
                System.out.println("Will Topic: " + willTopic);
                System.out.println("Will Message: " + willMessage);
            }
            
            // 7. 读取 Username（如果有）
            String username = null;
            if (usernameFlag) {
                if (payload.readableBytes() < 2) {
                    throw new IllegalArgumentException("Insufficient data for username length");
                }
                int usernameLen = payload.readUnsignedShort();
                if (payload.readableBytes() < usernameLen) {
                    throw new IllegalArgumentException("Insufficient data for username");
                }
                byte[] usernameBytes = new byte[usernameLen];
                payload.readBytes(usernameBytes);
                username = new String(usernameBytes, StandardCharsets.UTF_8);
                System.out.println("Username: " + username);
            }
            
            // 8. 读取 Password（如果有）
            String password = null;
            if (passwordFlag) {
                if (payload.readableBytes() < 2) {
                    throw new IllegalArgumentException("Insufficient data for password length");
                }
                int passwordLen = payload.readUnsignedShort();
                if (payload.readableBytes() < passwordLen) {
                    throw new IllegalArgumentException("Insufficient data for password");
                }
                byte[] passwordBytes = new byte[passwordLen];
                payload.readBytes(passwordBytes);
                password = new String(passwordBytes, StandardCharsets.UTF_8);
                System.out.println("Password: " + password);
            }
            
            // 9. 校验 username/password（这里仅示例，实际应查询数据库或配置）
            boolean authenticated = authenticateUser(username, password);
            
            // 10. 返回 CONNACK
            byte returnCode = authenticated ? (byte)0x00 : (byte)0x04;
            byte[] connackBytes;
            
            if (protocolLevel == 5) {
                // MQTT 5.0 CONNACK 格式:
                // [固定报头: 0x20] [剩余长度] [连接确认标志] [返回码] [Properties]
                // Properties 为空时，长度为 0x00
                connackBytes = new byte[]{
                    (byte)0x20,  // 固定报头: CONNACK
                    0x03,        // 剩余长度: 3字节 (连接确认标志1 + 返回码1 + Properties长度1)
                    0x00,        // 连接确认标志 (Session Present = 0)
                    returnCode,  // 返回码
                    0x00         // Properties 长度 = 0 (无属性)
                };
                System.out.println("=== Sending MQTT 5.0 CONNACK ===");
                System.out.println("CONNACK Bytes: " + bytesToHex(connackBytes));
                System.out.println("Return Code: " + returnCode + " (0=success, 4=bad username/password)");
            } else {
                // MQTT 3.1.1 CONNACK 格式:
                // [固定报头: 0x20] [剩余长度: 0x02] [连接确认标志] [返回码]
                connackBytes = new byte[]{
                    (byte)0x20,  // 固定报头: CONNACK
                    0x02,        // 剩余长度: 2字节
                    0x00,        // 连接确认标志 (Session Present = 0)
                    returnCode   // 返回码
                };
                System.out.println("=== Sending MQTT 3.1.1 CONNACK ===");
                System.out.println("CONNACK Bytes: " + bytesToHex(connackBytes));
                System.out.println("Return Code: " + returnCode + " (0=success, 4=bad username/password)");
            }
            
            // 发送 CONNACK 并等待完成
            ctx.writeAndFlush(Unpooled.wrappedBuffer(connackBytes)).addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("CONNACK sent successfully!");
                } else {
                    System.err.println("Failed to send CONNACK: " + future.cause());
                }
            });
            
            if (authenticated) {
                System.out.println("Client " + clientId + " connected successfully!");
                
                // 创建或更新会话
                ClientSession session = SessionManager.getInstance().createOrUpdateSession(
                    clientId, ctx.channel(), cleanSession, protocolLevel, username
                );
                System.out.println("Session created/updated: " + session);
            } else {
                System.out.println("Client " + clientId + " authentication failed!");
                ctx.close();
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            // 发送连接失败的 CONNACK
            byte[] connackBytes;
            // 尝试从异常上下文中获取 protocolLevel，如果没有则默认使用 MQTT 3.1.1
            // 这里简化处理，直接返回 MQTT 3.1.1 格式
            connackBytes = new byte[]{(byte)0x20, 0x02, 0x00, 0x01}; // 0x01 = 协议版本不支持
            ctx.writeAndFlush(Unpooled.wrappedBuffer(connackBytes));
            ctx.close();
        } finally {
            payload.release();
        }
    }
    
    /**
     * 校验用户名和密码
     * @param username 用户名
     * @param password 密码
     * @return 是否认证成功
     */
    private boolean authenticateUser(String username, String password) {
        // TODO: 实际应用中应查询数据库或配置文件
        // 这里仅作示例：允许任意用户连接，或者检查特定用户名密码
        if (username == null || password == null) {
            return false; // 如果需要认证但未提供，则拒绝
        }
        
        // 示例：允许 username="admin", password="admin123"
        return "rcAdmin".equals(username) && "admin@1234".equals(password);
    }
    
    /**
     * 解码 MQTT 协议的可变长度整数（Variable Byte Integer）
     * 用于 MQTT 5.0 的 Properties Length 等字段
     */
    private int decodeVariableByteInteger(ByteBuf buf) {
        int value = 0;
        int multiplier = 1;
        int digit;
        int count = 0;
        
        do {
            if (!buf.isReadable()) {
                throw new IllegalArgumentException("Incomplete variable byte integer");
            }
            digit = buf.readUnsignedByte();
            value += (digit & 0x7F) * multiplier;
            multiplier *= 128;
            count++;
            
            if (count > 4) {
                throw new IllegalArgumentException("Variable byte integer exceeds 4 bytes");
            }
        } while ((digit & 0x80) != 0);
        
        return value;
    }
    
    /**
     * 将字节数组转换为十六进制字符串（用于调试）
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        int limit = Math.min(bytes.length, 100); // 只显示前100字节
        for (int i = 0; i < limit; i++) {
            sb.append(String.format("%02X ", bytes[i]));
            if ((i + 1) % 16 == 0) sb.append("\n");
        }
        if (bytes.length > 100) {
            sb.append("... (" + bytes.length + " bytes total)");
        }
        return sb.toString();
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 客户端断开连接时移除会话
        ClientSession session = SessionManager.getInstance().getSessionByChannel(ctx.channel());
        if (session != null) {
            System.out.println("Client disconnected: " + session.getClientId());
            // 如果是 cleanSession，直接移除会话
            if (session.isCleanSession()) {
                SessionManager.getInstance().removeSessionByChannel(ctx.channel());
            } else {
                // 保留会话，但标记 Channel 为 null
                session.setChannel(null);
                System.out.println("Session retained for client: " + session.getClientId());
            }
        }
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace(); 
        ctx.close();
    }
}
